home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 5
/
Apprentice-Release5.iso
/
Source Code
/
Libraries
/
Advanced I⁄O v2.3
/
Advanced i⁄o
/
endian_io.cc
< prev
next >
Wrap
Text File
|
1996-02-02
|
9KB
|
298 lines
// This may look like C code, but it is really -*- C++ -*-
/*
************************************************************************
*
* Modified File class
* to read integers of various sizes taking the byte order
* into account
* and bit-stream IO
*
* $Id: endian_io.cc,v 2.1 1995/03/02 18:06:20 oleg Exp oleg $
*
************************************************************************
*/
#ifdef __GNUC__
#pragma implementation
#endif
#include "endian_io.h"
#include "std.h"
/*
*------------------------------------------------------------------------
* Error handling
*/
void EndianIOData::error(const char *descr)
{
if( eof() )
_error("'%s' failed because of the End-Of-File",descr);
perror(descr);
_error("Aborted due to the I/O error above");
}
void EndianIOData::assert_open(const char * file_name)
{
if( good() )
return;
perror("Opening error");
_error("Failed to open file '%s' because of the error above",file_name);
}
/*
*------------------------------------------------------------------------
* Opening/closing
*
* Note that when the EndianIO stream is shared, it shares also the i/o
* buffer with the existing stream. Care should be taken
* to not destroy the buffer when the attached stream is closed/destroyed.
* The situation is similar to what happens when a file handle is
* duplicated with the system function dup(). Note that creating a new
* file buffer for the attached stream based on the dup()-ed file
* handle is not enough. Indeed, the old (sample) stream could've read
* some data ahead in its buffer, so the system file pointer would not
* correspond to data that were actually read (and consumed) by the
* user. This situation can be partly remedied using a fstream::sync()
* function (which sets the system file pointer to correspond to what
* actually was read from the stream and discards the read-ahead data).
* However, when the attached stream reads from the file and advances the file
* pointer, the user of the original stream should be able to recognize
* the fact that the file was read by another stream. This problem cannot
* be solved withoud sharing the file buffer itself.
*/
void EndianIOData::share_with(EndianIOData& a_file)
{
#if 0 // Because of the stupid implementation of filebuf:
if( rdbuf() ) // filebuf is a part of the class fstream in GCC
delete rdbuf(); // Destroy the old stream buffer
#endif
init(a_file.rdbuf()); // Share the buffer of a_file
byte_order = a_file.byte_order;
shared = true;
clear();
}
// leave the stream open when it was attached
void EndianIn::close(void)
{
if( !was_shared() )
ifstream::close();
}
void EndianOut::close(void)
{
if( !was_shared() )
ofstream::close();
}
// If the stream was attached, detach it
// from the buffer
void EndianIOData::unshare(void)
{
if( was_shared() )
init(0), shared=false;
}
/*
*------------------------------------------------------------------------
* Reading routines
*/
// Read a SHORT (2 bytes) datum item
// in the specified byte_order
unsigned short int EndianIn::read_short(const char * op_description)
{
unsigned char c1, c2;
if( !get(c1) || !get(c2) ) // Read 2 consecutive bytes
error(op_description);
if( q_MSBfirst() )
return (c1 << 8) | c2;
else
return (c2 << 8) | c1;
}
// Read a LONG (4 bytes) datum item
// in the specified byte_order
unsigned long int EndianIn::read_long(const char * op_description)
{
unsigned char c1, c2, c3, c4;
if( !get(c1) || !get(c2) || // Read 4 consecutive bytes
!get(c3) || !get(c4) )
error(op_description);
if ( q_MSBfirst() )
return (c1 << 24) | (c2 << 16) | (c3 << 8) | c4;
else
return (c4 << 24) | (c3 << 16) | (c2 << 8) | c1;
}
/*
*------------------------------------------------------------------------
* Service writing routines
*/
void EndianOut::write_byte
(const int item, const char * op_description)
{
if( !put(item) )
error(op_description);
}
// Write out a short item as specified by
// the byte_order
void EndianOut::write_short
(const unsigned short item, const char * op_description)
{
#if 0 // The following does NOT work
union { // because it doesn't make sure
unsigned short int short_int; // that c2 follows c1
struct { unsigned char c1,c2; } bytes;
} int_bytes;
int_bytes.short_int = item;
write_byte(int_bytes.bytes.c2,"write_short, 2nd byte");
write_byte(int_bytes.bytes.c1,"write_short, 1st byte");
#endif
if( q_MSBfirst() )
write_byte((item>>8) & 0xff,"write_short, hi byte"),
write_byte(item & 0xff,"write_short, lo byte");
else
write_byte(item & 0xff,"write_short, lo byte"),
write_byte((item>>8) & 0xff,"write_short, hi byte");
}
// Write out a long item as specified by
// the byte_order
void EndianOut::write_long
(const unsigned long item, const char * op_description)
{
register unsigned int t = item;
register int i;
if( q_MSBfirst() )
for(i=24; i>=0; i-=8)
write_byte((t >> i) & 0xff,"write_long");
else
for(i=0; i<4; i++)
write_byte(t & 0xff,"write_long"), t >>= 8;
}
/*
*------------------------------------------------------------------------
* Reading/Writing signed short (16-bit) integers
* using variable-size code
* The code is intended for writing a collection of short integers where many
* of them are rather small in value; still, big values can crop up at times,
* so we can't limit the size of the code to anything less than 16 bits.
* The code is a variation of a start-stop code described in Appendix A,
* "Variable-length representations of the integers" of the "Text Compression"
* book by T.Bell, J.Cleary and I.Witten, p.290-295. The present code
* features support for both negative and positive numbers and optimization
* using a fact that all numbers are no larger than 2^15-1 in abs value
* and assumption that most of them smaller than 512 (in absolute value)
*
* Specifically, the code is as follows:
* For numbers not exceeding 63 in abs value, the code is
* 0 sign-bit 6-bits-of-abs-value (most significant bit first)
* For numbers within the range [64,511+64] in abs value, the code is
* 10 sign-bit 9-bits-of-(abs-value - 64)
* For numbers within the range [64+512,4095+512+64] in abs value, the code is
* 110 sign-bit 12-bits-of-(abs-value - 576)
* For bigger numbers, the code is
* 111 16-bits-of-value
* Thus the penalty is at most 3 extra bits (comparing with the plain 16-bit
* encoding) for *very* big numbers.
*/
// Write a signed short integer
// using a variable size code
void BitOut::put_short(short item)
{
int abs_val = abs(item);
if( !(abs_val & ~63) ) // small number
{
put_bit(0);
put_bit(item < 0);
for(register int i=32; i!=0; i>>=1)
put_bit(abs_val & i ? 1 : 0); // write 6 bits with MSB first
return;
}
if( !((abs_val-=64) & ~511) )
{
put_bit(1);
put_bit(0);
put_bit(item < 0);
for(register int i=256; i!=0; i>>=1)
put_bit(abs_val & i ? 1 : 0);
return;
}
if( !((abs_val-=512) & ~4095) )
{
put_bit(1);
put_bit(1);
put_bit(0);
put_bit(item < 0);
for(register int i=2048; i!=0; i>>=1)
put_bit(abs_val & i ? 1 : 0);
return;
}
put_bit(1); // Very big number: default case
put_bit(1);
put_bit(1); // Write all 16 bits with MSB first
for(register unsigned int i=(1<<15); i!=0; i>>=1)
put_bit(item & i ? 1 : 0);
}
// Get a short integer that was written
// using a variable size code
short BitIn::get_short(void)
{
if( !get_bit() ) // If the first bit turns out zero
{
const bool neg_sign = get_bit();
int val = 0;
for(register int i=32; i!=0; i>>=1) // Read 6 bits of abs value
if( get_bit() ) // (MSB first)
val |= i;
return neg_sign ? -val : val;
}
if( !get_bit() ) // First bit was 1, second is 0
{
const bool neg_sign = get_bit();
int val = 0;
for(register int i=256; i!=0; i>>=1)
if( get_bit() )
val |= i;
return val += 64, neg_sign ? -val : val;
}
if( !get_bit() ) // First two bits were 1, third is 0
{
const bool neg_sign = get_bit();
int val = 0;
for(register int i=2048; i!=0; i>>=1)
if( get_bit() )
val |= i;
return val += 64+512, neg_sign ? -val : val;
}
int val = 0; // First three bits were all ones
for(register unsigned int i=(1<<15); i!=0; i>>=1)
if( get_bit() )
val |= i;
return val;
}